// Copyright Epic Games, Inc. All Rights Reserved.

#include "SuperClipBoardApp.h"
#include "RequiredProgramMainCPPInclude.h"
#include "Widgets/Testing/STestSuite.h"
#include "Widgets/Testing/SStarshipSuite.h"
#include "Widgets/Testing/SPerfSuite.h"
#include "Widgets/Docking/SDockTab.h"
#include "Framework/Application/SlateApplication.h"
#include "StandaloneRenderer.h"
#include "Misc/TextFilterExpressionEvaluator.h"
#include "Styling/StarshipCoreStyle.h"

#include "Widgets/SMainWindow.h"

#if PLATFORM_WINDOWS
#include "Windows/WindowsWindow.h"
#include "Windows/WindowsCriticalSection.h"
#include "Windows/WindowsPlatformProcess.h"
#include <TlHelp32.h>
#elif PLATFORM_LINUX
#include "Linux/LinuxWindow.h"
#include "Linux/LinuxCriticalSection.h"
#include "Linux/LinuxPlatformProcess.h"
#include "SDL_video.h"	
#endif

#define LOCTEXT_NAMESPACE "SuperClipBoard"

IMPLEMENT_APPLICATION(SuperClipBoard, "SuperClipBoard");
namespace WorkspaceMenu
{
	TSharedRef<FWorkspaceItem> DeveloperMenu = FWorkspaceItem::NewGroup(LOCTEXT("DeveloperMenu", "Developer"));
}


int RunSuperClipBoard( const TCHAR* CommandLine )
{

	FTaskTagScope TaskTagScope(ETaskTag::EGameThread);	

	// start up the main loop
	GEngineLoop.PreInit(CommandLine);

	FCommandLine::Set(CommandLine);

	// Make sure all UObject classes are registered and default properties have been initialized
	ProcessNewlyLoadedUObjects();
	
	// Tell the module manager it may now process newly-loaded UObjects when new C++ modules are loaded
	FModuleManager::Get().StartProcessingNewlyLoadedObjects();
	FSuperClipBoardApplication::Exec();

	GEngineLoop.AppPreExit();
	GEngineLoop.AppExit();

	return 0;
}
#if PLATFORM_WINDOWS
typedef struct EnumHWndsArg
{
	TArray<HWND>* vecHWnds;
	uint32 dwProcessId;
}EnumHWndsArg, * LPEnumHWndsArg;

HANDLE GetProcessHandleByID(int nID)//通过进程ID获取进程句柄
{
	return OpenProcess(PROCESS_ALL_ACCESS, false, nID);
}

TArray<uint32> GetProcessIDByName(const FString& pName)
{
	TArray<uint32> ProcessIDRets;
	HANDLE hSnapshot = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
	if (INVALID_HANDLE_VALUE == hSnapshot) {
		return ProcessIDRets;
	}
	PROCESSENTRY32 pe = { sizeof(pe) };
	for (BOOL ret = ::Process32First(hSnapshot, &pe); ret; ret = ::Process32Next(hSnapshot, &pe)) {
		FString ExeFile = WCHAR_TO_TCHAR(pe.szExeFile);
		if (ExeFile == pName) {
			ProcessIDRets.Add(pe.th32ProcessID);
		}
		//printf("%-6d %s\n", pe.th32ProcessID, pe.szExeFile);
	}
	CloseHandle(hSnapshot);
	return ProcessIDRets;
}

BOOL CALLBACK lpEnumFunc(HWND hwnd, LPARAM lParam)
{
	EnumHWndsArg* pArg = (LPEnumHWndsArg)lParam;
	DWORD  processId;
	GetWindowThreadProcessId(hwnd, &processId);
	if (processId == pArg->dwProcessId)
	{
		pArg->vecHWnds->Push(hwnd);
		//printf("%p\n", hwnd);
	}
	return true;
}

void GetHWndsByProcessID(DWORD processID, TArray<HWND>& vecHWnds)
{
	EnumHWndsArg wi;
	wi.dwProcessId = processID;
	wi.vecHWnds = &vecHWnds;
	::EnumWindows(lpEnumFunc, (LPARAM)&wi);
}


void FSuperClipBoardApplication::AwakenMainWindow_Win()
{
#if WITH_EDITOR

	TArray<uint32> ProcessIDRets = GetProcessIDByName(TEXT("SuperClipBoard.exe"));
#else
	TArray<uint32> ProcessIDRets = GetProcessIDByName(TEXT("SuperClipBoard-Win64-Shipping.exe"));
#endif
	uint32 CurPid = FPlatformProcess::GetCurrentProcessId();
	UE_LOG(LogTemp, Log, TEXT("[FSuperClipBoardApplication::Exec] SuperClipBoard : CurPid:%p"), CurPid);
	for (const uint32 Pid : ProcessIDRets)
	{
		UE_LOG(LogTemp, Log, TEXT("[FSuperClipBoardApplication::Exec] SuperClipBoard : Pid:%p"), Pid);
		if (CurPid != Pid)
		{
			if (Pid != 0)
			{
				TArray<HWND> HWinds;
				GetHWndsByProcessID(Pid, HWinds);
				for (const HWND& Hwnd : HWinds)
				{
					HWND Parent = ::GetParent(Hwnd);
					if (Parent == NULL)
					{
						UE_LOG(LogTemp, Log, TEXT("[FSuperClipBoardApplication::Exec] Main Wnd: %p"), Hwnd);
						SwitchToThisWindow(Hwnd, true);
					}
					else
					{
						UE_LOG(LogTemp, Log, TEXT("[FSuperClipBoardApplication::Exec] Wnd: %p; Parent: %p"), Hwnd, Parent);
					}
				}
			}
			break;
		}
	}
}

#elif PLATFORM_LINUX

TArray<uint32> GetProcessIDByName(const FString& ExceName)
{
	TArray<uint32> ProcessIDRets;
	FString Cmd = FString::Printf(TEXT("ps -e | grep %s | awk \'{print $1}\'"),*ExceName);
	UE_LOG(LogTemp, Log, TEXT("[GetProcessIDByName Linux] Cmd: %s"), *Cmd);
	FILE* FilePtr = ::popen(TCHAR_TO_UTF8(*Cmd),"r");
	char Buffer[10] = {0};
	while (::fgets(Buffer, 10, FilePtr))
	{
		FString Temp = ANSI_TO_TCHAR(Buffer);
		uint32 Pid = FCString::Atoi(*Temp);
		ProcessIDRets.Add(Pid);
	}
	return ProcessIDRets;
}

TArray<uint32> GetWndIdsByPid(const uint32 Pid)
{
	TArray<uint32> WndIdRets;
	FString Cmd = FString::Printf(TEXT("xdotool search --any --pid %d | awk \'{print $1}\'"),Pid);
	UE_LOG(LogTemp, Log, TEXT("[GetWndIDsByPid Linux] Cmd: %s"), *Cmd);
	FILE* FilePtr = ::popen(TCHAR_TO_UTF8(*Cmd),"r");
	char Buffer[15] = {0};
	while (::fgets(Buffer, 15, FilePtr))
	{
		FString Temp = ANSI_TO_TCHAR(Buffer);
		UE_LOG(LogTemp, Log, TEXT("[GetWndIDsByPid Linux] Temp: %s"), *Temp);
		if (Temp.IsEmpty())
		{
			continue;
		}
		
		uint32 WndId = FCString::Atoi(*Temp);
		WndIdRets.Add(WndId);
	}
	return WndIdRets;
}

void FSuperClipBoardApplication::AwakenMainWindow_Linux()
{
	TArray<uint32> ProcessIDRets = GetProcessIDByName(TEXT("SuperClipBoard"));
	uint32 CurPid = FPlatformProcess::GetCurrentProcessId();
	UE_LOG(LogTemp, Log, TEXT("[FSuperClipBoardApplication::Exec] SuperClipBoard : CurPid:%p"), CurPid);
	for (const uint32 Pid : ProcessIDRets)
	{
		UE_LOG(LogTemp, Log, TEXT("[FSuperClipBoardApplication::Exec] SuperClipBoard : Pid:%p"), Pid);
		if (CurPid != Pid)
		{
			if (Pid != 0)
			{
				TArray<uint32> WndIdRets = GetWndIdsByPid(Pid);
				for(const uint32 WndId : WndIdRets)
				{
					UE_LOG(LogTemp, Log, TEXT("[FSuperClipBoardApplication::Exec] SuperClipBoard : Wnd:%p"), WndId);
					FString Cmd = FString::Printf(TEXT("xdotool windowactivate %p"),WndId);
					::system(TCHAR_TO_UTF8(*Cmd));
				}
			}
			break;
		}
	}
}

void FSuperClipBoardApplication::WindowminimizebyPid(const uint32 CurPid)
{
	TArray<uint32> WndIdRets = GetWndIdsByPid(CurPid);
	for(const uint32 WndId : WndIdRets)
	{
		UE_LOG(LogTemp, Log, TEXT("[FSuperClipBoardApplication::Exec] SuperClipBoard : Wnd:%p"), WndId);
		FString Cmd = FString::Printf(TEXT("xdotool windowminimize %p"),WndId);
		::system(TCHAR_TO_UTF8(*Cmd));
	}
}

#endif

#undef LOCTEXT_NAMESPACE

TSharedPtr<SMainWindow> FSuperClipBoardApplication::MainWindow = nullptr;
FSystemWideCriticalSection* FSuperClipBoardApplication::Check = nullptr;
FInsertNewItem FSuperClipBoardApplication::InsertNewItem;

void FSuperClipBoardApplication::Exec()
{
	// 防止多开 [8/18/2022 CarlZhou]
	Check = new FSystemWideCriticalSection(TEXT("SuperClipBoard"));
	if (!Check->IsValid())
	{
		//唤醒上一个程序，关闭当前程序
		AwakenMainWindow();
		FPlatformMisc::RequestExit(true);
	}
	//////////////////////////////////////////////////////////////////////////
	InitializeApplication();
	// loop while the server does the rest
	while (!IsEngineExitRequested())
	{
		BeginExitIfRequested();

		FTaskGraphInterface::Get().ProcessThreadUntilIdle(ENamedThreads::GameThread);
		FStats::AdvanceFrame(false);
		FTSTicker::GetCoreTicker().Tick(FApp::GetDeltaTime());
		FSlateApplication::Get().PumpMessages();
		FSlateApplication::Get().Tick();
		MainWindow->CustomTick();
		FPlatformProcess::Sleep(0.01);
	
		GFrameCounter++;
	}
	ShutdownApplication();
}

void FSuperClipBoardApplication::AwakenMainWindow()
{
#if PLATFORM_WINDOWS
	AwakenMainWindow_Win();
#elif PLATFORM_LINUX
	AwakenMainWindow_Linux();
#endif

}

void FSuperClipBoardApplication::InitializeApplication()
{
	// FCoreStyle::ResetToDefault();
	//TODO:初始化自己的Style

	// Load required modules.
	FModuleManager::Get().LoadModuleChecked("EditorStyle");

	// Load plug-ins.
	// @todo: allow for better plug-in support in standalone Slate applications
	IPluginManager::Get().LoadModulesForEnabledPlugins(ELoadingPhase::PreDefault);

	// crank up a normal Slate application using the platform's standalone renderer
	FSlateApplication::InitializeAsStandaloneApplication(GetStandardStandaloneRenderer());

	FSlateApplication::InitHighDPI(true);
	MainWindow = SNew(SMainWindow);
	FSlateApplication::Get().AddWindow(MainWindow.ToSharedRef());

}

void FSuperClipBoardApplication::ShutdownApplication()
{
	FCoreDelegates::OnExit.Broadcast();
	FSlateApplication::Shutdown();
	FModuleManager::Get().UnloadModulesAtShutdown();
}
